//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<editor_engine.cpp>
///	@path	~/src/editor/
///	@date	2008/10/23
///	@desc	.

#define _FULLAPI
#include "config/config.h"

#include "lib/utilities/observer.h"
#include "lib/system/redirect.h"
#include "lib/system/sysinfo.h"

#include "database/DataManager.h"
#include "database/xres/CLocalFileSystem.h"
#include "database/xres/CXmlFileHandler.h"
#include "database/xdata/CDirectoryData.h"
#include "database/xdata/CTreeData.h"

#include "graphics/dx9/xeres_dx9.h"
#include "graphics/dx10/xeres_dx10.h"
#include "graphics/dx9/import_dx9.h"
#include "graphics/IRenderer.h"
#include "graphics/canvas/CCanvasProxy.h"

#include "scene/IScene.h"
#include "scene/camera/CCameraBase.h"
#include "scene/xsimple/XSimple.h"

#include "engine/xcore/xeres_engine.h"
#include "engine/xcore/event_center.h"
#include "engine/xcore/CInputEvent.h"

#include "python/python_plug.h"
#include "python/interpreter/CPythonInterpreter.h"
#include "python/wrap/expose_python.h"

#include "lua/lua_plug.h"
#include "lua/interpreter/CLuaInterpreter.h"
#include "lua/wrap/expose_lua.h"

#include "gui/xgui/XCtrls.h"
#include "gui/xgui/XCtrlsExt.h"
#include "gui/xgui/XEditCtrl.h"
#include "gui/xgui/XTagLayout.h"

#include "editor/editor_engine.h"
#include "editor/wm/XEngineView.h"
#include "editor/wrap/expose_editor_to_python.h"

namespace xeres {

	class xeditor_helper
	{
	public:

		// This callback registers extension classes.
		static void editor_engine_init_reg_class( void )
		{
			TRACE_INFO( _S(".Init: Registering class...") );
			IOBJECT_REGISTER_CLASS( XSimple );
		}

		/// \brief	Initialize file system.
		static void editor_engine_init_file_system( XEditor * editor )
		{
			// Configuration file system.
			Ref<CLocalFileSystem> etc_filesystem = new CLocalFileSystem;
			etc_filesystem->SetPathBase( _S("/etc/") );
			Ref<CDirectoryData> etc_root = new CDirectoryData( etc_filesystem , _S("/") , _S("/etc/") );

			// Add root directory to engine.
			editor->engine().AddDataEntryRoot( etc_root );


			// Resource file system.
			Ref<CLocalFileSystem> res_filesystem = new CLocalFileSystem;
			res_filesystem->SetPathBase( _S("/res/") );
			Ref<CDirectoryData> res_root = new CDirectoryData( res_filesystem , _S("/") , _S("/res/") );
			
			// Add root directory to engine.
			editor->engine().AddDataEntryRoot( res_root );

			// Create XML file handler.
			Ref<CXmlFileHandler> xml = new CXmlFileHandler;

			// Register file handler.
			res_filesystem->AddFileHandler( _S("xml") , xml );
			etc_filesystem->AddFileHandler( _S("xml") , xml );

			// Extension for Scene loaders in resources.
			res_filesystem->AddFileHandler( _S("xsimple;xmodel") , xml );


			editor->m_etc = etc_filesystem;
			editor->m_res = res_filesystem;
		}

		// This callback initializes graphics device.
		// We use the class we just registered to create rendering environment.
		static void editor_engine_init_device( XEditor * editor )
		{
			TRACE_INFO( _S(".Init: Initializing device...") );

			Ref<IData> entry = editor->engine().OpenData( _S("etc/engine_config.xml") );

			int version = 9;
			if( entry.IsValid() )
			{
				version = entry->ReadInt( _S("device/version" ) , version );
			}

			bool enable_debug = false;
#ifndef _FINAL
			if( entry.IsValid() )
			{
				enable_debug = entry->ReadBool( _S("device/debug" ) , enable_debug );
			}
#endif

			// Register DX9 classes.
			LoadDirectX9( enable_debug );

			if( version > 9 && IS_OS_VISTA() )
			{
				// Register DX10 classes.
				LoadDirectX10( enable_debug );
				editor->engine().ConfigDevice( editor->m_engineWnd , _S("CDevice10") , _S("CRenderer10") , entry , entry );
			}
			else
			{
				// Create new graphics device by passing class name.
				editor->engine().ConfigDevice( editor->m_engineWnd , _S("CDevice9") , _S("CRenderer9") , entry , entry );
			}

			// Set default shading mode.
			editor->engine().ChangeShadingMode( _S("forward") );

			// Config shader include directory.
			editor->engine().GetRenderer()->AddInclude( editor->m_res , _S("fx/include") );
		}
		
		// This function creates & initializes script system.
		static void editor_engine_init_script_system( XEditor * editor )
		{
			TRACE_INFO( _S(".Init: Runing script...") );

			// Initialize python.
			PythonPlug::instance().Initialize( _S("xdemo") ,
				_S("script") , _S("script/lib;script/;script/common;script/editor") , editor->m_res );

			PythonPlug::instance().SetEncoding( "mbcs" );

			expose_basic_to_python();
			expose_database_to_python( &editor->engine() );
			expose_ui_to_python( NULL , &editor->engine() );
			expose_engine_to_python();
			expose_editor_to_python();

			// Run script initialization.
			// Run script initialization.
			try
			{
				boost::python::object module = boost::python::import( "xedit" );
				module.attr("init")();
			}
			catch( boost::python::error_already_set )
			{
				PyErr_Print();
				PyErr_Clear();
				FATAL_ABORT( _S("Script initialization failed (script error)...") );
			}

			// Initialize Lua.
			LuaPlug::instance().Initialize( _S("xdemo") , editor->m_res );

			expose_basic_to_lua();
		}

		// This call back initializes scene manager.
		static void editor_engine_init_scene_mgr( XEditor * editor )
		{
			TRACE_INFO( _S(".Init: Constructing scene...") );

			Ref<IData> entry = editor->engine().OpenData( _S("res/scene/scene_config.xml") );
			// Initializes scene manager.
			editor->engine().ConfigScene( entry );
		}

		// This call config multi-tasking of engine.
		static void editor_engine_init_multi_tasking( XEditor * editor )
		{
			TRACE_INFO( _S(".Init: Configurating multi-tasking...") );

			Ref<IData> entry = editor->engine().OpenData( _S("etc/engine_config.xml") );
			// Set tasking config.
			editor->engine().ConfigMultiTasking( entry );
		}

		// This call config input & message handling of engine.
		static void editor_engine_init_input( XEditor * editor , XEngineView * view )
		{
			TRACE_INFO( _S(".Init: Configurating input...") );
			view->AttachEngine( editor->engine() );

			// Subscribe messages.

			editor->engine().SubscribeEvent(
				_S("input/keyboard") , EVENTSLOT_APP , bind( editor_engine_on_key_input , editor , _1 ) );

			editor->engine().SubscribeEvent(
				_S("input/mouse") , EVENTSLOT_APP , bind( editor_engine_on_mouse_input , editor , _1 ) );

			editor->engine().SubscribeEvent(
				_S("message/activate") , EVENTSLOT_APP , bind( editor_engine_activate_window_call , editor , _1 ) );

			editor->engine().SubscribeEvent(
				_S("message/size") , EVENTSLOT_APP , bind( editor_engine_size_window_call , editor , _1 ) );

			editor->engine().SubscribeEvent(
				_S("message/paint") , EVENTSLOT_APP , bind( editor_engine_paint_window_call , editor , _1 ) );
		}

		// finalize script
		static void editor_engine_fini_script_system( XEditor * editor )
		{
			// finalize python
			PythonPlug::instance().Finalize();
			LuaPlug::instance().Finalize();
		}

		// This function handles key input event.
		static HandleEvent editor_engine_on_key_input( XEditor * editor , void * input )
		{
			return EVENT_UNHANDLED;
		};

		// This function handles mouse input event.
		static HandleEvent editor_engine_on_mouse_input( XEditor * editor , void * input )
		{
			return EVENT_UNHANDLED;
		};

		// Activate window callback.
		static HandleEvent editor_engine_activate_window_call( XEditor * editor , void * msg )
		{
			WindowEventInfo * info = static_cast<WindowEventInfo*>(msg);
			if( info->m_tag == WINDOW_EVENT_ACTIVATE )
			{
				editor->engine().SetFrameLimit( 0 );
			}
			else if( info->m_tag == WINDOW_EVENT_INACTIVATE )
			{
				editor->engine().SetFrameLimit( 1 );
			}
			else
			{
				return EVENT_UNHANDLED;
			}
			return EVENT_HANDLED;
		}

		// Size change callback.
		static HandleEvent editor_engine_size_window_call( XEditor * editor , void * msg )
		{
			WindowEventInfo * info = static_cast<WindowEventInfo*>(msg);
			static bool is_sizing = false;

			if( info->m_tag == WINDOW_EVENT_ENTERSIZING )
			{
				editor->engine().DeviceOnEnterSizing( editor->m_engineWnd );

				is_sizing = true;
			}
			else if( info->m_tag == WINDOW_EVENT_EXITSIZING )
			{
				editor->engine().DeviceOnExitSizing( editor->m_engineWnd );

				// Head up UI resize?.

				is_sizing = false;
			}
			else if( info->m_tag == WINDOW_EVENT_MINIMIZED )
			{
				editor->engine().DeviceOnSize( editor->m_engineWnd , true );
			}
			else // WINDOW_EVENT_NONE
			{
				editor->engine().DeviceOnSize( editor->m_engineWnd , false );

				// Head up UI resize?.
//				if( !is_sizing )
			}
			return EVENT_HANDLED;
		}

		// Paint callback.
		static HandleEvent editor_engine_paint_window_call( XEditor * editor , void * msg )
		{
			::ValidateRect( editor->m_engineWnd , NULL );
			return EVENT_UNHANDLED;
		}
	};

	// XEditor
	XEditor::XEditor( void )
		: m_engine( Engine::instance() )
	{
		// redirect
		redirect_stdio( _S("xeres_debug") ,
			trace_stdout , trace_stderr );
	}
	// ~XEditor
	XEditor::~XEditor( void )
	{

	}
	// instance
	XEditor& XEditor::instance( void )
	{
		static XEditor s_editorInstance;
		return s_editorInstance;
	}
	// Initialize
	void XEditor::Initialize( XEngineView * engineView )
	{
		HWND hWnd = engineView->m_hWnd;
		XS_ASSERT( ::IsWindow( hWnd ) );
		m_engineWnd = hWnd;

		// ----Initialization----

		// 1. Register classes.
		engine().QueueInitializer( _S("./register/xdemo") , _S("class") ,
			xeditor_helper::editor_engine_init_reg_class );

		// 2. Create filsystem for input/output.
		engine().QueueInitializer( _S("./filesystem") , _S("") ,
			bind( xeditor_helper::editor_engine_init_file_system , this ) );

		// 3. Initialize device.
		engine().QueueInitializer( _S("./device") , _S("filesystem") ,
			bind( xeditor_helper::editor_engine_init_device , this ) );

		// 6. Initialize scene manager.
		engine().QueueInitializer( _S("./scene") , _S("device") ,
			bind( xeditor_helper::editor_engine_init_scene_mgr , this ) );

		// 7. Initialize multi-tasking.
		engine().QueueInitializer( _S("./thread") , _S("scene") ,
			bind( xeditor_helper::editor_engine_init_multi_tasking , this ) );

		// 9. Initialize script system.
		engine().QueueInitializer( _S("./script") , _S("thread") ,
			bind( xeditor_helper::editor_engine_init_script_system , this ) );

		// 10. Initialize script system.
		engine().QueueInitializer( _S("./input") , _S("script") ,
			bind( xeditor_helper::editor_engine_init_input , this , engineView ) );

		// ----Finalization----

		// 2. Finalize script system.
		engine().QueueFinalizer( _S("~/script") , _S("") ,
			bind( xeditor_helper::editor_engine_fini_script_system , this ) );

		// start engine
		m_engine.Initialize();

		// editor initialize
		{

		}

		// first frame
		m_engine.FirstFrame();

		m_camera = new CCameraBase;
		m_engine.SetCamera( m_camera );
		// instant a simple scene
		engine().InstantScene( _S("simple") );
	}
	// Finalize
	void XEditor::Finalize( void )
	{
		// last frame
		m_engine.LastFrame();

		// editor finalize
		{
			m_res = NULL;
			m_etc = NULL;
		}

		m_engine.Finalize();
	}

} // namespace xeres
